﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using NFit.Core.Interfaces;
using log4net;
using NFit.Core.Utils;

namespace NFit.Core.DataSources
{
    /// <summary>
    /// Data source based on file system
    /// </summary>
    public class FileSystemDataSource : ISpecificationDataSource
    {
        private static ILog Logger = log4net.LogManager.GetLogger(typeof(FileSystemDataSource));

        private List<SpecificationDefinition> TestList;

        /// <summary>
        /// The name of the data source
        /// </summary>
        public string Name
        {
            get; set;
        }

        /// <summary>
        /// The specification reader for the data source
        /// </summary>
        public ISpecificationReader Reader
        {
            get; set;
        }

        /// <summary>
        /// The root directory for the file source
        /// </summary>
        public string RootDirectory { get; set; }

        private string _searchPattern = "*.fit";

        /// <summary>
        /// The root directory for the file source
        /// </summary>
        public string SearchPattern
        {
            get { return _searchPattern; }
            set { _searchPattern = value; }
        }

        /// <summary>
        /// Indicate if the data source must inspect sub directories
        /// </summary>
        public bool SearchSubDirectories { get; set; }

        /// <summary>
        /// Get the list of root specifications
        /// </summary>
        /// <returns></returns>
        public IEnumerable<SpecificationDefinition> GetTestDefinitions()
        {
            if (TestList == null)
            {
                TestList = new List<SpecificationDefinition>();
                LoadTests();
            }
            return TestList;
        }

        /// <summary>
        /// Load the data from a directory
        /// </summary>
        private void LoadTests()
        {
            Logger.Debug("Loading specs on root path " + RootDirectory + " search pattern " + SearchPattern);
            Performances.Measure(Logger, "Folder " + RootDirectory + "(" + SearchPattern + ") loaded in {0}ms", () =>
            {
                string path = RootDirectory;
                if (string.IsNullOrEmpty(path))
                {
                    path = AppDomain.CurrentDomain.BaseDirectory;
                }

                LoadFromDirectory(null, path, SearchPattern, SearchSubDirectories);
            });
        }

        /// <summary>
        /// Load the data from a directory
        /// </summary>
        /// <param name="parentTest"></param>
        /// <param name="dirPath"></param>
        /// <param name="testFileSearchPattern"></param>
        /// <param name="searchSubdirectories"></param>
        private void LoadFromDirectory(SpecificationDefinition parentTest, string dirPath, string testFileSearchPattern, bool searchSubdirectories)
        {
            string[] files = Directory.GetFiles(dirPath, testFileSearchPattern);
            List<string> ScannedDirectories = new List<string>();

            foreach(string s in files)
            {
                SpecificationDefinition test = LoadSpec(s);
                if (test != null)
                {
                    if (parentTest == null)
                    {
                        TestList.Add(test);                        
                    }
                    else
                    {
                        Logger.Debug("Adding child specs " + test.Name + " to " + parentTest.Name);
                        parentTest.AddChildSpec(test);
                    }

                    SearchForSubSpecs(dirPath, testFileSearchPattern, s, test, searchSubdirectories, ScannedDirectories);
                }
            }

            if (searchSubdirectories)
            {
                string[] subdirs = Directory.GetDirectories(dirPath);
                foreach(string s in subdirs)
                {
                    if (!ScannedDirectories.Contains(s))
                    {
                        LoadFromDirectory(parentTest, s, testFileSearchPattern, searchSubdirectories);
                    }
                }
            }
        }

        private void SearchForSubSpecs(string dirPath, string testFileSearchPattern, string testFile, SpecificationDefinition spec, bool searchSubdirectories, List<string> ScannedDirectories)
        {
            if (searchSubdirectories)
            {
                string testfilename = Path.GetFileNameWithoutExtension(testFile);
                string testdir = Path.Combine(dirPath, testfilename);
                if (Directory.Exists(testdir))
                {
                    ScannedDirectories.Add(testdir);
                    LoadFromDirectory(spec, testdir, testFileSearchPattern, searchSubdirectories);
                }
            }
        }

        private SpecificationDefinition LoadSpec(string specFileName)
        {
            SpecificationDefinition test = null;
            Logger.Debug("Loading specs from file " + specFileName);
            Performances.Measure(Logger, "File loaded in {0}ms (" + specFileName + ")",()=>
            {
                using (FileStream stream = File.OpenRead(specFileName))
                {
                    test = Reader.ReadSpecification(stream);
                    test.Name = Path.GetFileNameWithoutExtension(specFileName);
                }                                                                                               
            });

            return test;
        }

        /*
        public ExecutionSummary RunTests()
        {
            IExecutionContext context = ServiceLocator.Resolve<IExecutionContext>();
            IEnumerable<SpecificationDefinition> tests = GetTestDefinitions();

            foreach (var test in tests)
            {
                SpecificationExecution testRun = new SpecificationExecution(test, context);
                testRun.Run();
            }

            return context.Summary;
        }*/
    }
}
